package com.zy.chat.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zy.chat.dto.LoginFormDTO;
import com.zy.chat.dto.UserDTO;
import com.zy.chat.entity.Recommend;
import com.zy.chat.entity.UserInfo;
import com.zy.chat.mapper.UserMapper;
import com.zy.chat.service.UserService;
import com.zy.chat.util.UserHolder;
import com.zy.chat.vo.RequestParams;
import com.zy.chat.vo.UserResult;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import static com.zy.chat.util.RedisContants.*;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserInfo> implements UserService {


    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Override
    public List<UserInfo> getFriendList(String userId) {
        return  userMapper.getFriendList(userId);
    }


    @Override
    public UserResult search(RequestParams params) {
        //准备Request
        SearchRequest request = new SearchRequest("user");
        //准备请求参数
        buildBasicQuery(params,request);
        //发送请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return handleResponse(response);
    }

    @Override
    public List<String> getSuggestion(String key) {
        SearchRequest request = new SearchRequest("user");//准备请求
        request.source().suggest(new SuggestBuilder().addSuggestion(
                "userSuggest",
                SuggestBuilders.completionSuggestion("suggestion")
                        .skipDuplicates(true)
                        .prefix(key)
        ));//请求参数
        //发出请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //解析
        Suggest suggest = response.getSuggest();
        //根据名称过去结果
        CompletionSuggestion suggestion =suggest.getSuggestion("userSuggest");
        //获取options
        ArrayList<String> list = new ArrayList<>();
        for(CompletionSuggestion.Entry.Option option:suggestion.getOptions()){
            //获取补全的结果
            String str = option.getText().toString();
            //放入集合
            list.add(str);
        }
    return list;
    }

    @Override
    public List<UserInfo> recommended(UserInfo user) {
        List<UserInfo> friends = getFriendList(user.getUserId()); // 获取用户的好友列表
        List<Recommend> recommendedFriends = new ArrayList<>();

        for (UserInfo friend : friends) {
            Recommend recommend = new Recommend();
            recommend.setUser(friend);
            List<UserInfo> friendFriends = getFriendList(friend.getUserId()); // 获取好友的好友列表
            if(friendFriends.size()==0) continue;
            Set<UserInfo> intersection = new HashSet<>(friendFriends);
            intersection.retainAll(friends); // 取两个列表的交集
            double degreeScore = intersection.size() / (double) friendFriends.size(); // 计算度分数
            double friendOfFriendDegreeScore = getFriendOfFriendDegreeScore(user, friend); // 计算好友的好友的度分数
            double finalScore = degreeScore * friendOfFriendDegreeScore; // 计算最终权重
            recommend.setRecommendationScore(finalScore); // 设置好友的推荐得分
            recommendedFriends.add(recommend);
        }
        recommendedFriends.sort(Comparator.comparing(Recommend::getRecommendationScore).reversed()); // 根据推荐得分排序
        return recommendedFriends.stream().map(Recommend::getUser).distinct().limit(2).collect(Collectors.toList());
    }

    @Override
    public UserDTO person(String username) {

        UserInfo one = query().eq("username", username).one();
        UserDTO userDTO = BeanUtil.copyProperties(one, UserDTO.class);
        return  userDTO;
    }

    @Override
    public Boolean sign() {
        String username = UserHolder.getUser().getUsername();
        LocalDateTime now = LocalDateTime.now();
        String keyEnd =now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key=USER_SIGN_KEY+username+keyEnd;
        int dayNow=now.getDayOfMonth();
        redisTemplate.opsForValue().setBit(key,dayNow-1,true);
        return true;
    }

    @Override
    public Integer signcount() {
        // 1.获取当前登录用户
        String username = UserHolder.getUser().getUsername();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = USER_SIGN_KEY + username + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        List<Long> list = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType
                        .signed(dayOfMonth)
                ).valueAt(0)
        );
        if(list==null||list.isEmpty()){
            return 0;
        }

        Long num=list.get(0);
        if(num==null||num==0){
            return 0;
        }
        int count=0;
        while (true){
            if((num&1)!=1){
                break;
            }
            count++;
            num>>>=1;
        }
        return count;
    }

    private double getFriendOfFriendDegreeScore(UserInfo user, UserInfo friend) {
        List<UserInfo> friendFriends = getFriendList(friend.getUserId()); // 获取好友的好友列表
        double friendOfFriendDegreeSum = 0;
        for (UserInfo friendFriend : friendFriends) {
            if (!friendFriend.equals(user) && !isFriend(user, friendFriend)) { // 排除已经是用户好友的人和用户自己
                friendOfFriendDegreeSum += getDegreeCentrality(friendFriend); // 累加好友的好友的度中心性
            }
        }
        return friendOfFriendDegreeSum / friendFriends.size(); // 计算好友的好友的度分数
    }
    public Boolean isFriend(UserInfo user,UserInfo friendFriend){
        return getFriendList(friendFriend.getUserId()).contains(user);
    }

    private double getDegreeCentrality(UserInfo user) {
        List<UserInfo> friends = getFriendList(user.getUserId()); // 获取用户的好友列表
        int degreeSum = friends.size();
        for (UserInfo friend : friends) {
            degreeSum += getFriendList(friend.getUserId()).size(); // 计算所有好友的好友数之和
        }
        return degreeSum / (double) (friends.size() + 1); // 计算度中心性
    }

    private UserResult handleResponse(SearchResponse response) {
        SearchHits searchHits = response.getHits();
        //总条数
        long total=searchHits.getTotalHits().value;
        //获取文档数组
        SearchHit[] hits = searchHits.getHits();
        //遍历
        ArrayList<UserInfo> users = new ArrayList<>(hits.length);
        for(SearchHit hit:hits){
            //获取source
            String json=hit.getSourceAsString();
            //反序列化，非高亮的
            UserInfo userInfo = JSON.parseObject(json, UserInfo.class);
            //处理高亮结果
            //获取高亮map
            Map<String, HighlightField> map = hit.getHighlightFields();
            if (map != null && !map.isEmpty()) {
                // 2）根据字段名，获取高亮结果
                HighlightField highlightField = map.get("username");
                if (highlightField != null) {
                    // 3）获取高亮结果字符串数组中的第1个元素
                    String hName = highlightField.getFragments()[0].toString();
                    // 4）把高亮结果放到HotelDoc中
                    userInfo.setUsername(hName);
                }
            }
            // 4.9.放入集合
            users.add(userInfo);
        }
        return new UserResult(total,users);
    }

    public void buildBasicQuery(RequestParams params,SearchRequest request){
        //准备Boolean查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //关键字搜索
        String key=params.getKey();
        if(StringUtils.isNotBlank(key)){
            boolQuery.must(QueryBuilders.matchQuery("username",key));
        }
        //设置查询条件
        request.source().query(boolQuery);
    }


}
